{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# РЕГРЕССИЯ И КЛАССИФИКАЦИЯ"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Предсказание цены на подержанные автомобиль Ford"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "pEWXJykcVpHE"
   },
   "source": [
    "**Бизнес-постановка задачи** \n",
    "\n",
    "Оценка подержанного автомобиля - это достаточно трудная задача, так как на стоимость влияют различные факторы, например, возраст автомобиля, его состояние, пробег и даже личное отношение продавца. Таким образом, цена подержанных автомобилей на рынке не является постоянной. И поскольку нет прозрачности в её образовании, а спрос растет ежегодно, у нечестных предпринимателей возникает стимул иррационально завышать цену. Модель для оценки стоимости подержанного автомобиля помогла бы покупателям не переплатить за желаемое авто, а честным продавцам быстро устанавливать цену, соответствующую их предложениям. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "AA2SyJehVpHF"
   },
   "source": [
    "**Постановка задачи анализа данных** \n",
    "\n",
    "Целью данной задачи является прогнозирование цены на подержанные автомобили Ford с помощью построения регрессионных моделей и их анализа. Набор данных состоит из информации о транспортных средствах, выставленных на продажу на сайте Craigslist. Данные опубликованы в открытом доступе на платформе Kaggle. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "0yo9SRN4VpHG"
   },
   "source": [
    "**Обзор доступных данных**\n",
    "\n",
    "В выборке 4913 наблюдений и 12 характеристик для каждого из объектов (штат продажи, год выпуска, технические характеристики автомобиля, цена транспортного средства и т.д.). Пустые значения указывают на то, что о соответствующей характеристики нет информации. Выборка была разбита на две части для обучения и для тестирования модели."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "__Br3ARWVpHG"
   },
   "source": [
    "Итак, данные содержат два типа переменных:\n",
    "\n",
    "* Целевая: **price**\n",
    "* Остальные переменные: **11 переменных, которые могут использоваться для прогноза целевой переменной.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "n1keP6JCVpHH"
   },
   "source": [
    "## План анализа данных (data mining):\n",
    "\n",
    "  1. Загрузить данные для обучения\n",
    "  2. Обработать данные перед обучением модели\n",
    "  3. Обучить модель на обучающей выборке\n",
    "  4. Загрузить и предобработать данные для тестирования\n",
    "  5. Провалидировать модель на тестовой выборке"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "aoG95e54VpHI"
   },
   "source": [
    "## 1. Загрузить данные для обучения"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "EUclkaPTVpHJ"
   },
   "source": [
    "**Шаг 1.1. Загружаем библиотеки** "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "53HanWonVpHK"
   },
   "outputs": [],
   "source": [
    "import warnings\n",
    "warnings.filterwarnings(\"ignore\")\n",
    "import pandas as pd \n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import seaborn as sns\n",
    "sns.set_style('whitegrid')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "gActSs3PVpHT"
   },
   "source": [
    "**Шаг 1.2. Загрузим данные**\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "p2BMYCwYRYTw"
   },
   "source": [
    "Для решения задачи мы будем использовать данные. Они состоят из двух частей: часть для обучения и часть для тестирования модели."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "KmyZ77fr2tZ_"
   },
   "outputs": [],
   "source": [
    "training_data = pd.read_excel('training_data_ford.xlsx')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 204
    },
    "id": "8_UxTfoC2wMg",
    "outputId": "eb606fcd-7427-46f5-89d5-783209399eb5"
   },
   "outputs": [],
   "source": [
    "training_data.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Сделаем так, чтобы при загрузке не использовался столбец `Unnamed: 0`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "ihpn24vxVpHU"
   },
   "outputs": [],
   "source": [
    "training_data = pd.read_excel('training_data_ford.xlsx', usecols=lambda x: 'Unnamed' not in x) # загружаем таблицу в переменную training_data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 204
    },
    "id": "1UeBDq89VpHX",
    "outputId": "3c1947bb-eda2-4c9d-d17b-1c149be6f52e"
   },
   "outputs": [],
   "source": [
    "training_data.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "0EL90ZdWVpHa"
   },
   "source": [
    "Ниже в таблице представлено описание каждого из 12 полей."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "-cBO9ImLVpHb"
   },
   "source": [
    "|Название поля \t |Описание      \t                              |Название поля   |Описание                                  |\n",
    "|:--------------:|:----------------------------------------------:|:--------------:|:----------------------------------------:|\n",
    "|**price**       |Цена                                            |**transmission**|Коробка передач                           |\n",
    "|**year**        |Год производства \t                              |**drive**       |Привод                                    |\n",
    "|**condition**   |Состояние        \t                              |**size**        |Полноразмер или нет                       |\n",
    "|**cylinders**   |Количество цлиндров \t                          |**lat**         |Широта \t                                  |\n",
    "|**odometer**    |Пробег                                          |**long**        |Долгота  \t                              |\n",
    "|**title_status**|Легальный статус авто  (все документы в наличии)|**weather**     |Среднегодовая температура в городе продажи|\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "VwAsIpuOVpHc"
   },
   "source": [
    "**Шаг 1.3. Посмотрим на размеры загруженной таблицы**, у которой мы видели только первые 5 строк.\n",
    "\n",
    "Для этого вызываем поле **shape** у нашей переменной *training_data*. Поле вызывается также как метод, но в конце скобки не ставятся, так как для поля не предусмотрена передача аргументов.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "pjF7eVBiVpHc",
    "outputId": "764c67e4-394f-4642-9a58-6997222cfb6a"
   },
   "outputs": [],
   "source": [
    "training_data.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "iW3qRGZcVpHg"
   },
   "source": [
    "# 2. Обработать данные перед обучением модели"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "fW9eRYgoVpHh"
   },
   "source": [
    "<a href=\"https://drive.google.com/uc?id=1oBVbNi9xUsQObgLV0fA0oP4To5AbLc7j\n",
    "\" target=\"_blank\"><img src=\"https://drive.google.com/uc?id=1oBVbNi9xUsQObgLV0fA0oP4To5AbLc7j\" \n",
    "alt=\"IMAGE ALT TEXT HERE\" width=\"360\" border=\"0\" /></a>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "6me6mH7LVpHh"
   },
   "source": [
    "**Шаг 2.1. Проверяем данные на наличие пропусков и типов переменных**\n",
    "\n",
    "Начнем с проверки общей информации о данных.\n",
    "Для того чтобы это сделать, нужно обратиться вызвать у переменной *training_data* метод **info()**.\n",
    "\n",
    "Напомним, что в конце необходимо поставить скобочки."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "7ZOawvYOVpHi",
    "outputId": "714452f6-1694-4bce-dbf1-fbb4fa154953"
   },
   "outputs": [],
   "source": [
    "training_data.info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "erHCAu_rVpHl"
   },
   "source": [
    "Анализируем результата выполнения команды:\n",
    "\n",
    "* 4913 строк (entries)\n",
    "* 12 столбцов (Data columns)\n",
    "\n",
    "В данных присутствует три типа dtypes:\n",
    "* int64 - целое число  (5 столбцов)\n",
    "* float64 - дробное число (3 столбца)\n",
    "* object - не число, обычно текст (4 столбца)\n",
    "\n",
    "В нашем случае признаки с типом object имеют текстовые значения. \n",
    "\n",
    "Цифры в каждой строчке обозначают количество заполненных (*non-null*) значений. Видно, что в данных содержатся пропуски, так как эти цифры не в каждой строчке совпадают с полным числом строк (4913)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "_bq3PXRbVpHl"
   },
   "source": [
    "**Шаг 2.2. Удаляем пропуски**\n",
    "\n",
    "Как мы уже видели выше, в наших данных есть пропуски (значения NaN). Для удобства работы выкинем такие данные из нашего датасета, применив метод **dropna()** к *training_data*:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "8R6DHvBLVpHm"
   },
   "outputs": [],
   "source": [
    "training_data = training_data.dropna()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "reY3T3MwVpHp"
   },
   "source": [
    "Посмотрим на то, как изменились размеры таблички:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "bfKiVzXuVpHp",
    "outputId": "4af23160-39ce-482c-b991-1537b62ecde5"
   },
   "outputs": [],
   "source": [
    "training_data.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "jva_6UWSVpHs"
   },
   "source": [
    "Также, после выкидывания строк с пропущенными значениями осталось 3659 строка из 4913. Нам повезло: наш набор данных был заполнен на 75%. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "2xZ4SSKaVpHs"
   },
   "source": [
    "**Шаг 2.3. Отделяем текстовые признаки от числовых**\n",
    "\n",
    "Мы видим, что некоторые характеристики представлены текстовыми значениями. Машине сложно обрабатывать текстовые признаки, поэтому для упрощения работы мы оставим столбцы, содержащие только числовые данные.   \n",
    "\n",
    "Отметим, что, таким образом мы можем потерять довольно много информации (например, очевидно, что тип привода (признак drive)  влияет на стоимость), однако мы сознательно идем на это упрощение на данном этапе обучения. \n",
    "\n",
    "Чтобы получить все числовые характеристики, необхдимо применить метод **_get_numeric_data()** к объекту *training_data*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "e6RinDt_VpHt"
   },
   "outputs": [],
   "source": [
    "training_data = training_data._get_numeric_data()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "rWe-0cAwVpHv"
   },
   "source": [
    "Посмотрим на данные еще раз. Теперь они содержат лишь числовые признаки."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 204
    },
    "id": "Ft7MTQn1VpHw",
    "outputId": "5b901059-6637-4880-840d-277c0df29e20"
   },
   "outputs": [],
   "source": [
    "training_data.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "P0221IXpVpHz"
   },
   "source": [
    "Итак, из 12 столбцов у нас осталось 8 числовых, 4 текстовых мы убрали."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "DjWJBfeIVpH1"
   },
   "source": [
    "Для числовых признаков можно построить гистограмму. Гистограмма - это способ графического представления табличных данных, благодаря которому можно увидеть распределение значений признака. \n",
    "\n",
    "Для построения гистограммы необходимо вызвать метод **hist()** у объекта *training_data*. Желательно указать аргумент *figsize*, который устанавливает ожидаемый размер изображения. В нашем случае это (15,15).  \n",
    "\n",
    "Заметим, что название переменной, по которой строится гистограмма, указано в названии графика."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 879
    },
    "id": "rH0Bn63FVpH2",
    "outputId": "e9d81a74-97ca-422d-df79-c24c51e72ba3"
   },
   "outputs": [],
   "source": [
    "training_data.hist(figsize=(15, 15));"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "iOCJ9GQOVpH9"
   },
   "source": [
    "Например, рассмотрим признак cylinders. Из гистограммы видно, что у нас очень мало машин с четырьмя и десятью цилиндрами. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "paek7UNfVpH-"
   },
   "source": [
    "**Шаг 2.4. Работаем с целевой переменной**\n",
    "\n",
    "*Какая переменная целевая?*\n",
    "\n",
    "В данном случае по условию задачи мы должны прогнозировать стоимость автомобиля, поэтому целевая переменная - это price. Запишем название целевого признака в отдельную переменную с названием *target_variable_name*. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "KGs58XjDVpIA"
   },
   "outputs": [],
   "source": [
    "target_variable_name = 'price'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "rCgsDIpZVpID"
   },
   "source": [
    "Нам нужно выделить в отдельную переменную *training_values* столбец из нашей таблицы, который соответствует определенной выше целевой переменной. Для этого мы у таблицы *training_data* в квадратных скобках указываем имя нужного столбца. В нашем случае это имя записано в переменной *target_variable_name*. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "LtVZZ4zKVpIE"
   },
   "outputs": [],
   "source": [
    "training_values = training_data[target_variable_name]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "TLL7PuCCVpIG"
   },
   "source": [
    "Отделим входные переменные от выходной (целевой), чтобы можно было построить модель предсказания целевой переменной по входным. \n",
    "Для это нужно у переменной *training_data* вызвать метод **drop()**. Результат мы записываем в новую переменную *training_points*. После выполнения запроса *training_points* будет содержать исходную таблицу без целевого столбца. \n",
    "\n",
    "Обратите внимание, что в данном случае мы передаем два аргумента:\n",
    "    1. target_variable_name - название столбца цены, который мы ранее записали в эту переменную и теперь хотим удалить из training_data\n",
    "    2. axis=1 - означает, что мы удаляем столбец, а в случае axis=0 - означает, что мы удаляем строку"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "sBSDO6TtVpIG"
   },
   "outputs": [],
   "source": [
    "training_points = training_data.drop(target_variable_name, axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "3yxSxAkWVpII"
   },
   "source": [
    "Можно посмотреть результаты этих действий, вызвав метод **head()** и поле **shape**, которыми мы пользовались ранее, но сейчас нужно вызывать их от новой переменной *training_points*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 204
    },
    "id": "8c26aeLBVpIJ",
    "outputId": "9a85d9fc-49fa-4e32-eb0f-2a5138f9b041"
   },
   "outputs": [],
   "source": [
    "training_points.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "zoxp0WQbVpIL",
    "outputId": "22d73583-2fc7-4912-aa46-d11389e3cc7c"
   },
   "outputs": [],
   "source": [
    "training_points.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "yU79ZTVDVpIN"
   },
   "source": [
    "Видно, что столбца действительно нет, а количество строк не изменилось. Данные в 5 первых строках такие же, как были ранее."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ToU5Hrg6VpIO"
   },
   "source": [
    "##   3. Обучить модель на обучающей выборке"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "vrrvfCDOVpIO"
   },
   "source": [
    "![](https://raw.githubusercontent.com/MerkulovDaniil/TensorFlow_and_Keras_crash_course/master/ford_price.png)\n",
    "\n",
    "**Шаг 3.1. Выбираем метод, который будем использовать**\n",
    "\n",
    "Проще всего начать с простых методов. \n",
    "Мы воспользуемся двумя методами для построения моделей и сравним их между собой:\n",
    "* Линейная регрессия *linear regression*\n",
    "* Лес решающих деревьев *random forest*\n",
    "\n",
    "На выбор метода для построения модели влияет набор признаков, размер выборки, интуиция про то, какая связь между входными переменными и целевой. Но часто решение принимается исходя из того, какая модель сработала лучше."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Iz5FMkMbVpIP"
   },
   "source": [
    "Для корректной работы с методами построения моделей в python требуется загрузить специальную библиотеку\n",
    "**sklearn**, программную библиотеку на языке python для для машинного обучения и анализа данных.\n",
    "\n",
    "Мы импортируем два модуля из этой библиотеки:\n",
    " * *linear_model* - тут находятся все линейные модели\n",
    " * *ensemble* - тут находятся модели на основе ансамблей"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "s3geYEN6VpIP"
   },
   "outputs": [],
   "source": [
    "from sklearn import linear_model, ensemble"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ZAlTULkpVpIR"
   },
   "source": [
    "Прежде чем начать делать ремонт, нужно подготовить инструменты для работы. Аналогично в нашем случае, прежде чем обучать модели, нужно создать их прототипы.  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "PC-7xBQ0VpIT"
   },
   "source": [
    "Чтобы создать модель линейной регресии, пишем имя модуля `linear_model`, затем точку, затем название модели.\n",
    "\n",
    "Для этого нужно выполнить следующий код:\n",
    "\n",
    "```python\n",
    "linear_regression_model = linear_model.LinearRegression()\n",
    "linear_regression_model\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "aNnSF784VpIU"
   },
   "outputs": [],
   "source": [
    "linear_regression_model = linear_model.LinearRegression() # создаем модель"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "dDsnG2khVpIV",
    "outputId": "83d289dd-bdad-4cb0-ab76-6f04a4cb6564"
   },
   "outputs": [],
   "source": [
    "linear_regression_model # смотрим, что получилось"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "linear_regression_model.get_params()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Aexks5k2VpIX"
   },
   "source": [
    "Чтобы создать модель случайного леса, пишем имя модуля `ensemble`, затем точку, затем название модели. \n",
    "\n",
    "Для этого нужно выполнить следующий код:\n",
    "\n",
    "```python\n",
    "random_forest_model = ensemble.RandomForestRegressor()\n",
    "random_forest_model\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "KBf_WQtKVpIY"
   },
   "source": [
    "Обратите внимание, что для воспроизводимости результата на разных компьютерах необходимо для всех зафиксировать один параметр random_state. Например, можно установить для него значение 123. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "tch7UIN6VpIY",
    "outputId": "eb7dcc42-efa8-494f-8ccb-70a4560487b8"
   },
   "outputs": [],
   "source": [
    "random_forest_model = ensemble.RandomForestRegressor(random_state=123)\n",
    "random_forest_model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "random_forest_model.get_params()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "2CbmYUXaVpIa"
   },
   "source": [
    "У модели на основе случайного леса больше параметров. Рассмотрим наиболее важные:\n",
    "* параметр *n_estimators* определяет, сколько деревьев в лесу,\n",
    "* в параметре *max_depth* устанавливается, какая максимальная глубина у дерева,\n",
    "* в параметре *min_samples_leaf* задается, какое максимальное число объектов может попасть в лист дерева."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "vD6S-2h8VpIb"
   },
   "source": [
    "Так как у модели на основе случайного решающего леса больше параметров, такая модель обычно обучается медленнее.\n",
    "Кроме этого, на время обучения влияют значения параметров модели. Например, чем больше деревьев в лесе - тем дольше модель будет учиться."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ygy1NGS7VpIc"
   },
   "source": [
    "**Шаг 3.2. Обучить модель**\n",
    "\n",
    "Теперь, когда мы создали прототипы обеих моделей, можем их обучить с помощью обучающей выборки. \n",
    "\n",
    "Для этого вызываем метод **fit()** у каждой модели и передаем ему на вход два аргумента: \n",
    "таблицу входных признаков и столбец значений целевой переменной - (training_points, training_values)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "6wpfnJJNVpIc",
    "outputId": "fd0c66a7-2bf8-4894-f43b-5f7e915d3ea6"
   },
   "outputs": [],
   "source": [
    "linear_regression_model.fit(training_points, training_values)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "m4-ZzGT7VpIf"
   },
   "source": [
    "Делаем тоже самое для модели решающего леса."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "rEyDntqDVpIg",
    "outputId": "0ef547cc-e914-4363-9ebf-6cfcde80e012"
   },
   "outputs": [],
   "source": [
    "random_forest_model.fit(training_points, training_values)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "qiQwJqMLVpIj"
   },
   "source": [
    "* Для двух разных моделей в sklearn методы для обучения модели не отличаются.\n",
    "* Мы получили две обученные модели. \n",
    "* Теперь необходимо провалидировать модели на новых тестовых данных. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "JIhETBmGVpIk"
   },
   "source": [
    "## 4. Загрузить и предобработать данные для тестирования"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "T4JbfVqbVpIl"
   },
   "source": [
    "**Шаг 4.1. Загрузим и проанализируем тестовые данные.**\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "yAx4akj5VpIl"
   },
   "outputs": [],
   "source": [
    "test_data = pd.read_excel('test_data_ford.xlsx', usecols=lambda x: 'Unnamed' not in x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 204
    },
    "id": "s7qyLLFEVpIn",
    "outputId": "91a9e66b-6231-4541-a9f9-b72d93a244d0"
   },
   "outputs": [],
   "source": [
    "test_data.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "KO34mMItVpIq",
    "outputId": "a9fddb2e-1e73-44f9-d249-67af91a8882a"
   },
   "outputs": [],
   "source": [
    "test_data.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "6PHjjnESVpIu"
   },
   "source": [
    "Проверим, есть ли в данных пропуски. Для того чтобы это сделать, нужно обратиться вызвать у переменной *test_data* метод **info()**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "lBCj7SgIVpIu",
    "outputId": "878d4736-bd21-4e5d-bc01-436b9094f4b7"
   },
   "outputs": [],
   "source": [
    "test_data.info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "_oHOMOi0VpIz"
   },
   "source": [
    "Нам необходимо удалить пропуски. Для этого применяем метод dropna() к test_data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "NG-omz7TVpI0"
   },
   "outputs": [],
   "source": [
    "test_data = test_data.dropna()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "zF2i7hJwVpI3"
   },
   "source": [
    "Также нам нужно получить все числовые характеристики, для этого необхдимо применить метод **_get_numeric_data()** к объекту *test_data*:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "Te59cOcUVpI4"
   },
   "outputs": [],
   "source": [
    "test_data = test_data._get_numeric_data()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "aaWonH5tVpI5"
   },
   "source": [
    "**Шаг 4.2. Отделяем целевую переменную**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "HOBamP4jVpI6"
   },
   "outputs": [],
   "source": [
    "test_values = test_data[target_variable_name]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "RoGzS1cmVpI9"
   },
   "outputs": [],
   "source": [
    "test_points = test_data.drop(target_variable_name, axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ZlLtvXxXVpI_"
   },
   "source": [
    "И проверяем результат записанный в test_points:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 204
    },
    "id": "cnFLyoV1VpJB",
    "outputId": "ed283f3f-cfdf-418a-d51f-6250454a7776"
   },
   "outputs": [],
   "source": [
    "test_points.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "ZaBU40bqVpJE",
    "outputId": "bd98fd92-5011-4339-d15b-0838b0bbfe81"
   },
   "outputs": [],
   "source": [
    "test_points.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "fZY7oIau7X5b",
    "outputId": "f63e7b54-7ed2-4403-8d8e-3e3fc5adc822"
   },
   "outputs": [],
   "source": [
    "list(test_points)==list(training_points)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "q1_dom14VpJO"
   },
   "source": [
    "# 5. Провалидировать модель на тестовой выборке"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ZqHZwM4NVpJO"
   },
   "source": [
    "**Шаг 5.1. Сравнение моделей.**\n",
    "\n",
    "Теперь мы готовы сравнить качество двух моделей! 😎"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "szK1iJdlVpJP"
   },
   "source": [
    "*1. Какая модель лучше?*\n",
    "\n",
    "Получим прогнозы целевой переменной на тестовых данных для модели линейной регрессии м модели случайного леса. \n",
    "\n",
    "Для этого вызовем у каждой модели метод **predict()**, в качестве аргумента передадим *test_points*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "CzyXhjjmVpJP"
   },
   "outputs": [],
   "source": [
    "test_predictions_linear = linear_regression_model.predict(test_points)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "mQgJKsCzVpJR"
   },
   "outputs": [],
   "source": [
    "test_predictions_random_forest = random_forest_model.predict(test_points)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "uzRLsLwzVpJT"
   },
   "source": [
    "Качество регрессионных моделей оценим двумя способами: \n",
    "1. Сравним визуально прогнозы с настоящими ценами (тестовые с предсказанием)\n",
    "2. Сравним метрики качества"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "4zLnfS-QVpJT"
   },
   "source": [
    "Визуализируем прогноз линейной модели и настоящие значения из тестовой выборки"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 451
    },
    "id": "NHenFn_xVpJU",
    "outputId": "37e72a9c-2484-489f-b76e-6c3a9cc26429"
   },
   "outputs": [],
   "source": [
    "plt.figure(figsize=(7, 7))\n",
    "plt.scatter(test_values, test_predictions_linear) # рисуем точки, соответствущие парам настоящее значение - прогноз\n",
    "plt.plot([0, max(test_values)], [0, max(test_values)])  # рисуем прямую, на которой предсказания и настоящие значения совпадают\n",
    "plt.xlabel('Настоящая цена', fontsize=20)\n",
    "plt.ylabel('Предсказанная цена', fontsize=20);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "h3sJ0r0EVpJW"
   },
   "source": [
    "Визуализируем прогноз модели случайного леса и настоящие значения из тестовой выборки"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 451
    },
    "id": "5F2x26VhVpJX",
    "outputId": "feed5fc6-c774-4e60-9388-7f7fa707cce8"
   },
   "outputs": [],
   "source": [
    "plt.figure(figsize=(7, 7))\n",
    "plt.scatter(test_values, test_predictions_random_forest) # рисуем точки, соответствущие парам настоящее значение - прогноз\n",
    "plt.plot([0, max(test_values)], [0, max(test_values)]) # рисуем прямую, на которой предсказания и настоящие значения совпадают\n",
    "plt.xlabel('Настоящая цена', fontsize=20)\n",
    "plt.ylabel('Предсказанная цена', fontsize=20);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "bz1bGYtBVpJY"
   },
   "source": [
    "Кажется, что лучше сработала модель случайного леса, так как точки на втором изображении расположены вдоль диагонали. На первом изображении видно, что для высоких настоящих цен модель линейной регрессии дает существенно заниженный результат. \n",
    "\n",
    "Проверим, так ли это с помощью **метрик качества регрессионной модели**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "1lxaLw0sVpJZ"
   },
   "source": [
    "Для корректного подсчета метрик качества модели в python требуется загрузить их из библиотеки **sklearn**. \n",
    "\n",
    "Мы используем три метрики качества:\n",
    " * *mean_absolute_error* - средняя абсолютная ошибка $MAE=\\sum\\limits_{i=1}^{n}\\frac{|y_i - \\hat{y}_i|}{n}$\n",
    " * *mean_squared_error* - средняя квадратичная ошибка $MSE=\\sum\\limits_{i=1}^{n}\\frac{(y_i - \\hat{y}_i)^2}{n}$\n",
    " * *r2_score* - коэффициент детерминации $R^2={\\frac{\\sum\\limits_{i=1}^{n}(\\hat{y}_i-\\bar{y})^2}{\\sum\\limits_{i=1}^{n}(y_i-\\bar{y})^2}}={1-\\frac{\\sum\\limits_{i=1}^{n}(y_i-\\hat{y}_i)^2}{\\sum\\limits_{i=1}^{n}(y_i-\\bar{y})^2}}$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "gkEpYLFeVpJZ"
   },
   "outputs": [],
   "source": [
    "from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "dBORSjO3VpJb"
   },
   "source": [
    "Подсчитаем ошибки для линейной модели.\n",
    "\n",
    "Для этого вызовем методы **mean_absolute_error()** и **mean_squared_error()**. На вход им передается столбец настоящих значений *test_values* и столбец значений, предсказанных моделью линейной регрессии *test_predictions_linear*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "r7-CM5OQVpJb"
   },
   "outputs": [],
   "source": [
    "mean_absolute_error_linear_model = mean_absolute_error(test_values, test_predictions_linear)\n",
    "mean_squared_error_linear_model = mean_squared_error(test_values, test_predictions_linear)\n",
    "r2_score_linear_model = r2_score(test_values, test_predictions_linear)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "6XGtVtRRVpJe"
   },
   "source": [
    "Подсчитаем ошибки для модели случайного леса.\n",
    "\n",
    "Для этого вызовем методы **mean_absolute_error()** и **mean_squared_error()**. На вход им передается столбец настоящих значений *test_values* и столбец значений, предсказанных моделью линейной регрессии *test_predictions_random_forest*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "BvhoMtppVpJf"
   },
   "outputs": [],
   "source": [
    "mean_absolute_error_random_forest_model = mean_absolute_error(test_values, test_predictions_random_forest)\n",
    "mean_squared_error_random_forest_model = mean_squared_error(test_values, test_predictions_random_forest)\n",
    "r2_score_random_forest_model = r2_score(test_values, test_predictions_random_forest)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "auOjxKrbVpJh"
   },
   "source": [
    "Теперь напечатаем полученные ошибки."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "6HtHlEomVpJi",
    "outputId": "ad5e540f-35b8-4a68-be74-53229c603ffd"
   },
   "outputs": [],
   "source": [
    "print(\"MAE: {0:7.2f}, RMSE: {1:7.2f}, R2: {2:7.2f} для линейной модели\".format(\n",
    "        mean_absolute_error(test_values, test_predictions_linear), \n",
    "        mean_squared_error(test_values, test_predictions_linear)**0.5, \n",
    "        r2_score_linear_model))\n",
    "\n",
    "print(\"MAE: {0:7.2f}, RMSE: {1:7.2f}, R2: {2:7.2f} для модели случайного леса\".format(\n",
    "       mean_absolute_error(test_values, test_predictions_random_forest), \n",
    "       mean_squared_error(test_values, test_predictions_random_forest)**0.5, \n",
    "       r2_score_random_forest_model))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "training_data['price'].mean()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "nPg94NvVVpJp"
   },
   "source": [
    "Модель случайного леса работает лучше и визуально, и потому, что абсолютная и средне квадратичная ошибка меньше для линейной регресии.\n",
    "\n",
    "Мы получили значения метрик ошибок наших моделей. Чтобы понять, насколько это нас утсраивает, важно взглянуть на исходный порядок цен на автомобили.\n",
    "Видно, что средняя цена имеет порядок 15 000 долларов, что означает, что полученная ошибка может удовлетворять предъявляемым требованиям к модели регрессии."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "8CehO5d0VpJq"
   },
   "source": [
    "## 6. Попробуем добавить в данные категориальные признаки?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "xDYxE1MwVpJr"
   },
   "source": [
    "Загрузим данные еще раз и выбросим строки с пропусками:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "00wepwNiVpJr"
   },
   "outputs": [],
   "source": [
    "training_data = pd.read_excel('training_data_ford.xlsx', usecols=lambda x: 'Unnamed' not in x)\n",
    "training_data = training_data.dropna()\n",
    "\n",
    "test_data = pd.read_excel('test_data_ford.xlsx', usecols=lambda x: 'Unnamed' not in x)\n",
    "test_data = test_data.dropna()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Ptfvmh1YVpJt"
   },
   "source": [
    "Посмотрим, что все загрузилось правильно:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 204
    },
    "id": "tAlSU3GwVpJu",
    "outputId": "9934505f-e44b-4b79-99ad-fed0ac9f6427"
   },
   "outputs": [],
   "source": [
    "training_data.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "AjHIS-O2VpJ3"
   },
   "source": [
    "Категориальный признак - это такой признак, который может принимать одно значение из ограниченного числа возможных. \n",
    "\n",
    "* В наших данных есть два *числовых* категориальных признаков: condition, cylinders \n",
    "\n",
    "* И несколько *текстовых* категориальных признаков: title_status, transmission, drive, size. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "9wVrMgiDVpJ4"
   },
   "source": [
    "Машине сложно обрабатывать текстовые признаки, поэтому нам необходимо закодировать их, то есть преобразовать в числовые.  \n",
    "\n",
    "Например, для категориального поля `transmission` значения были из множества `[\"automatic\", \"manual\", \"other\"]`. Мы изменим их на `[\"1\", \"2\", \"3\"]` соответственно."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "T1C3nN9IVpJ6"
   },
   "source": [
    "<a href=\"https://drive.google.com/uc?id=1G-FeQGfSRYMiWQCFBKj7x93IP8Hr_u_g\n",
    "\" target=\"_blank\"><img src=\"https://drive.google.com/uc?id=1G-FeQGfSRYMiWQCFBKj7x93IP8Hr_u_g\" \n",
    "alt=\"IMAGE ALT TEXT HERE\" width=\"360\" border=\"0\" /></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "zzisqc4_VpJ9"
   },
   "source": [
    "Пример кодирования для категориального признака Category, принимающего одно из четырех возможных значений ['Human', 'Penguin', 'Octopus', 'Alien']."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "MPahXDt6VpJ9"
   },
   "source": [
    "Для кодирования воспользуемся функцией **LabelEncoder()** из библиотеки **sklearn**. \n",
    "\n",
    "Сначала её нужно импортировать:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "bsahx_gaVpJ-"
   },
   "outputs": [],
   "source": [
    "from sklearn.preprocessing import LabelEncoder"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "WurWeY42VpJ_"
   },
   "source": [
    "Мы будем преобразовывать все текстовые категориальные признаки. Для удобства создадим отдельный список *text_categor_cols* с названиями признаков, которые мы хотим закодировать. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "jbZkuIrCVpKA"
   },
   "outputs": [],
   "source": [
    "text_categor_cols = ['title_status', 'transmission', 'drive', 'size']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "lMfus0edVpKB"
   },
   "source": [
    "Использование функции LabelEncoder() очень похоже на работу с моделями, которую мы демонстрировали выше. Прежде чем трансформировать текст в числа, необходимо создать прототип кодировщика. \n",
    "\n",
    "Это делается следующим образом:\n",
    "\n",
    "```python \n",
    "le = LabelEncoder()\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "iihrFaxkVpKB"
   },
   "outputs": [],
   "source": [
    "label_encoder = LabelEncoder()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "r0cGn9AUVpKD"
   },
   "source": [
    "Для того чтобы научить объект *label_encoder* кодировать один признак, нужно вызвать у него метод **fit_transform()** и в качестве аргумента передать значения признака. Как можно догадаться, этот метод состоит из двух частей: сначала *label_encoder* учится кодировать признак, то есть выполняет *fit*, затем применяет к нему полученную систему кодирования, выполняет *transform*. Так как тестовые данные нам нужно преобразовывать точно также, как и обучающие, то для тестовых признаков мы выполняем только *transform*. Для этого нужно вызвать у *label_encoder* метод **transform()** и в качестве аргумента передать значения признака из тестовой выборки."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "wSNWEU5YVpKD"
   },
   "source": [
    "Так как нам нужно закодировать сразу список признаков, мы будем делать это в цикле. Рассматриваем каждый текстовый признак из списка *text_categor_cols*, далее:\n",
    "1. методу **fit_transform()** передаем в качестве аргумента этот признак у обучающей выборки\n",
    "2. методу **transform()** передаем в качестве аргумента этот признак у тестовой выборки\n",
    "\n",
    "К полученным числовым представлениям признака будем прибавлять единичку, чтобы кодирование начиналось с 1, а не с 0. Затем результат будем записывать в табличку. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "7ct8GmTOVpKE"
   },
   "outputs": [],
   "source": [
    "for col in text_categor_cols:\n",
    "    training_data[col] = label_encoder.fit_transform(training_data[col]) + 1\n",
    "    test_data[col] = label_encoder.transform(test_data[col]) + 1\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "EV29F3j9VpKF"
   },
   "source": [
    "Посмотрим на данные теперь:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 359
    },
    "id": "tifMyFcpVpKG",
    "outputId": "25a9006e-6fc6-46d9-97b4-028cbd935a3e"
   },
   "outputs": [],
   "source": [
    "training_data.head(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "rHaa5VdHDJzn",
    "outputId": "2fbe6106-cdc4-4bed-dea7-73e67cf7ee8c"
   },
   "outputs": [],
   "source": [
    "training_data['drive'].value_counts()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "mj_iolSPVpKH"
   },
   "source": [
    "Все текстовые переменные теперь преобразованы в числа. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "y1-hGByZVpKI"
   },
   "source": [
    "Разделим данные на переменные и метки, как раньше:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "sBWKSDqKVpKI"
   },
   "outputs": [],
   "source": [
    "training_values = training_data[target_variable_name]\n",
    "training_points = training_data.drop(target_variable_name, axis=1)\n",
    "\n",
    "test_values = test_data[target_variable_name]\n",
    "test_points = test_data.drop(target_variable_name, axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "O8kl_MxDVpKJ"
   },
   "source": [
    "Мы уже знаем, что на этих данных лучше работает модель случайного леса, поэтому теперь будем использовать только её. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "W0eIyoaXVpKK",
    "outputId": "505abf62-cf5e-4b94-f778-5f620d6bfb0e"
   },
   "outputs": [],
   "source": [
    "random_forest_model = ensemble.RandomForestRegressor(random_state=42)\n",
    "random_forest_model.fit(training_points, training_values)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "aLloH5C_VpKM"
   },
   "source": [
    "Теперь заставим модели предсказывать на тестовых данных, результат запишем в переменную *test_predictions_random_forest_le*:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "0xIrOwXnVpKN"
   },
   "outputs": [],
   "source": [
    "test_predictions_random_forest_le = random_forest_model.predict(test_points)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "0rMH3i2XVpKO"
   },
   "source": [
    "Подсчитаем ошибку"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "dICppciZVpKP",
    "outputId": "69131f70-db18-4cbc-8387-a474533331f7"
   },
   "outputs": [],
   "source": [
    "print('Числовые + текстовые признаки')\n",
    "print(\"MAE: {0:7.2f}, RMSE: {1:7.2f}, R2: {2:7.2f} для модели случайного леса\".format(\n",
    "       mean_absolute_error(test_values, test_predictions_random_forest_le), \n",
    "       mean_squared_error(test_values, test_predictions_random_forest_le)**0.5,\n",
    "       r2_score(test_values, test_predictions_random_forest_le)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "mB-D_wWnVpKU"
   },
   "source": [
    "Сравним со значениями без использования категориальных признаков (результат лежит в переменной *test_predictions_random_forest*):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "g1PpJRA4VpKV",
    "outputId": "4db2c6b4-ef62-4ff9-9907-664595a19d70"
   },
   "outputs": [],
   "source": [
    "print('Только числовые признаки')\n",
    "print(\"MAE: {0:7.2f}, RMSE: {1:7.2f}, R2: {2:7.2f} для модели случайного леса\".format(\n",
    "       mean_absolute_error(test_values, test_predictions_random_forest), \n",
    "       mean_squared_error(test_values, test_predictions_random_forest)**0.5, \n",
    "       r2_score(test_values, test_predictions_random_forest)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "PrWyD-qSVpKW"
   },
   "source": [
    "Как видно, добавление текстовых закодированных признаков улучшило работу модели, так как значения метрик меньше. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "zcCtaSXSVpKX"
   },
   "source": [
    "Визуализируем прогноз модели случайного леса и настоящие значения из тестовой выборки с использованием текстовых признаков (синие точки) и без их использования (красные точки). "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 451
    },
    "id": "nLES9JdtVpKX",
    "outputId": "b1123eb0-bd8c-45c5-c125-977b61ac5574"
   },
   "outputs": [],
   "source": [
    "plt.figure(figsize=(7, 7))\n",
    "plt.scatter(test_values, test_predictions_random_forest_le, alpha=0.9, label='Числовые + текстовые признаки');\n",
    "plt.scatter(test_values, test_predictions_random_forest, color='orange', alpha=0.4, label='Только числовые признаки');\n",
    "plt.plot([0, max(test_values)], [0, max(test_values)]);\n",
    "\n",
    "plt.legend()\n",
    "plt.xlabel('Настоящая цена', fontsize=20)\n",
    "plt.ylabel('Предсказанная цена', fontsize=20);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "3yGg--L_VpKa"
   },
   "source": [
    "Из графика видно, что мы стали лучше предсказывать стоимость автомобилей с высокой настоящей ценой."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 519
    },
    "id": "GKTRFjl74s6T",
    "outputId": "de8230b3-304f-43be-9cab-174442dcc3a9"
   },
   "outputs": [],
   "source": [
    "linear_regression_model = linear_model.LinearRegression()\n",
    "linear_regression_model.fit(training_points, training_values)\n",
    "test_linear_regression_model_le = linear_regression_model.predict(test_points)\n",
    "\n",
    "print('Числовые + текстовые признаки')\n",
    "print(\"MAE: {0:7.2f}, RMSE: {1:7.2f}, R2: {2:7.2f} для линейной модели\".format(\n",
    "       mean_absolute_error(test_values, test_linear_regression_model_le), \n",
    "       mean_squared_error(test_values, test_linear_regression_model_le)**0.5,\n",
    "       r2_score(test_values, test_linear_regression_model_le)))\n",
    "print('Только числовые признаки')\n",
    "print(\"MAE: {0:7.2f}, RMSE: {1:7.2f}, R2: {2:7.2f} для линейной модели\".format(\n",
    "       mean_absolute_error(test_values, test_predictions_linear), \n",
    "       mean_squared_error(test_values, test_predictions_linear)**0.5,\n",
    "       r2_score(test_values, test_predictions_linear)))\n",
    "\n",
    "plt.figure(figsize=(7, 7))\n",
    "plt.scatter(test_values, test_linear_regression_model_le, alpha=0.9, label='Числовые + текстовые признаки');\n",
    "plt.scatter(test_values, test_predictions_linear, color='orange', alpha=0.4, label='Только числовые признаки');\n",
    "plt.plot([0, max(test_values)], [0, max(test_values)]);\n",
    "\n",
    "plt.legend()\n",
    "plt.xlabel('Настоящая цена', fontsize=20)\n",
    "plt.ylabel('Предсказанная цена', fontsize=20);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Импортируем инструменты для визуализации\n",
    "from sklearn.tree import export_graphviz\n",
    "from sklearn.ensemble import RandomForestRegressor\n",
    "# Визаулизируем одно дерево из леса деревьев\n",
    "import pydot\n",
    "training_data_list = list(training_points.columns)\n",
    "rf_big = RandomForestRegressor(n_estimators=100)\n",
    "rf_big.fit(training_points, training_values)\n",
    "rf_small = RandomForestRegressor(n_estimators=100, max_depth = 3)\n",
    "rf_small.fit(training_points, training_values)\n",
    "# Получаем большое дерево\n",
    "tree_big = rf_big.estimators_[5]\n",
    "# Получаем маленькое дерево\n",
    "tree_small = rf_small.estimators_[5]\n",
    "# Сохраняем в виде png изображения\n",
    "export_graphviz(tree_big, out_file = 'big_tree.dot', feature_names = training_data_list, rounded = True, precision = 1)\n",
    "(graph, ) = pydot.graph_from_dot_file('big_tree.dot')\n",
    "graph.write_png('big_tree.png')\n",
    "export_graphviz(tree_small, out_file = 'small_tree.dot', feature_names = training_data_list, rounded = True, precision = 1)\n",
    "(graph, ) = pydot.graph_from_dot_file('small_tree.dot')\n",
    "graph.write_png('small_tree.png')\n",
    "\n",
    "# Покажем извлеченное дерево решений\n",
    "from IPython.display import Image\n",
    "Image(filename = 'small_tree.png')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "feature_importance = pd.DataFrame(columns = ['Название признака', 'Важность признака'])\n",
    "feature_importance['Название признака'] = training_points.keys()\n",
    "feature_importance['Важность признака'] = rf_small.feature_importances_"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "feature_importance.sort_values(by='Важность признака', ascending=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Предсказание дефолта по кредиту\n",
    "\n",
    "Многие люди берут кредит в банке. Некоторые не отдают. Если просрочка по кредиту больше 90 дней, банк считает, по данному кредиту произошел дефолт, то есть клиент не в состоянии его отдать.\n",
    "\n",
    "Одна из первых задач, которую решало машинное обучение в банках - предсказание дефолта.\n",
    "Нужно по данным, которые предоставил потенциальный заемщик, определить, будет у него дефолт или нет.\n",
    "\n",
    "Задача предсказания дефолта - задача классификации, а результат работы модели для конкретного клиента - предсказать произойдет дефолт или нет.\n",
    "В этой тетради мы рассмотрим вопрос измерения качества работы обученных\n",
    "классификаторов. На примерах мы опишем различные метрики качества и способы\n",
    "их подсчёта. Также обратим внимание на адекватность их использования в типичных\n",
    "практических кейсах."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Мы попробуем на данных из Kaggle соревнования _\"Give me some credit\"_ обучить модель машинного обучения, которая будет предсказывать дефолт.\n",
    "https://www.kaggle.com/c/GiveMeSomeCredit#description"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Бизнес-постановка задачи** \n",
    "\n",
    "Банк по анкетным данным оценивает вероятность того, что для конкретного клиента произойдет дефолт.\n",
    "Применение модели ясно:\n",
    "* мы хотим выдавать кредиты только хорошим заемщикам, которые отдадут кредит."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Постановка задачи анализа данных** \n",
    "\n",
    "Целью данной задачи является построение модели *классификации дефолтов*: на вход модель будет принимать данные о клиенте, а на выходе она должна работать в двух режимах:\n",
    "* выдавать вероятность дефолта для данного клиента,\n",
    "* выдавать правильный с точки зрения модели класс клиента (есть у него дефолт или нет).\n",
    "\n",
    "Обучать модель мы будем по данным с платформы kaggle."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Обзор доступных данных**\n",
    "\n",
    "В выборке N наблюдений и 11 переменных, одна из которых - целевая. \n",
    "Таким образом, про каждого из N клиентов мы знаем значения 11 их характеристик (возраст, доход в месяц), в том числе значение целевой переменной: есть ли у клиента сейчас просрочка более 90 дней.\n",
    "\n",
    "Выборка была разбита на две части для обучения и для тестирования модели."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Доступные признаки**\n",
    "\n",
    "Данные содержат два типа переменных:\n",
    "\n",
    "* Целевая: **SeriousDlqin2yrs**, есть ли просрочка 90 дней и более\n",
    "* Остальные переменные: 10 переменных, могут использоваться для прогноза целевой переменной.\n",
    "\n",
    "| Имя столбца        | Значение |\n",
    "| :-------------: |:-------------:|\n",
    "| SeriousDlqin2yrs      | **Целевая переменная:** Есть ли просрочка 90 дней и более |\n",
    "| RevolvingUtilizationOfUnsecuredLines      | Доля использованных лимитов по кредитным картам     |\n",
    "| age | Возраст заемщика в годах |\n",
    "| DebtRatio | Отношение суммы долговой нагрузки, расходов на жизнь и алименты к доходу |\n",
    "| MonthlyIncome | Доход в месяц |\n",
    "| NumberOfOpenCreditLinesAndLoans | Количество открытых кредитов и кредитных линий (кредитных карт) |\n",
    "| NumberRealEstateLoansOrLines | Количество ипотек и других кредитных продуктов, связанных с недвижимостью |\n",
    "| NumberOfTime30-59DaysPastDueNotWorse | Сколько раз за последние 2 года у заемщика была просрочка 30-59 дней |\n",
    "| NumberOfTime60-89DaysPastDueNotWorse | Сколько раз за последние 2 года у заемщика была просрочка 60-89 дней |\n",
    "| NumberOfTimes90DaysLate | Сколько раз за последние 2 года у заемщика была просрочка более 90 дней |\n",
    "| NumberOfDependents | Количество иждивенцев в семье (супруг, дети и т.п.) |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## План анализа данных (data mining):\n",
    "\n",
    "  1. Загрузить данные для обучения\n",
    "  2. Обработать данные перед обучением модели\n",
    "  3. Обучить модель на обучающей выборке\n",
    "  4. Загрузить и предобработать данные для тестирования\n",
    "  5. Провалидировать модель на тестовой выборке"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Загрузить данные для обучения"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Шаг 1.1. Загружаем библиотеки** \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import warnings\n",
    "warnings.filterwarnings(\"ignore\")\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt \n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import seaborn as sns\n",
    "sns.set_style('whitegrid')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Библиотека **scikit-learn**. Выберем из нее:\n",
    "* классификатор дерево решений (**DecisionTreeClassifier**);\n",
    "* несколько готовых функции для расчёта метрик качества классификации."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.tree import DecisionTreeClassifier # классификатор дерева решений\n",
    "\n",
    "from sklearn.metrics import roc_curve, precision_recall_curve, auc # метрики качества\n",
    "from sklearn.metrics import confusion_matrix, f1_score, accuracy_score # метрики качества\n",
    "from sklearn.metrics import average_precision_score # метрики качества"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Шаг 1.2. Загрузим данные**\n",
    "\n",
    "Для решения задачи мы будем использовать данные. Они состоят из двух частей: часть для обучения и часть для тестирования модели."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "_cell_guid": "3ab4c525-a5cb-4183-9468-c1dd005c4c78",
    "id": "ni39CtSD5yb8"
   },
   "outputs": [],
   "source": [
    "training_data = pd.read_csv('training_data_defolt.csv')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "RccdlHBB5yb_"
   },
   "source": [
    "Посмотрим на 10 случайно выбранных записей из обучающего набора, для этого будем использовать функцию **sample()**. Параметр\n",
    "**random_state=123** фиксирует \"случайность\", то есть на любом компьютере метод **sample()** будет работать одинаково. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 396
    },
    "id": "rD2nn0xi5yb_",
    "outputId": "cce17ce9-c4be-4505-8d60-9b31bee9534e"
   },
   "outputs": [],
   "source": [
    "training_data.sample(10, random_state=123)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "8wpDIg0f5ycC"
   },
   "source": [
    "**Шаг 1.3. Посмотрим общую статистику по данным**\n",
    "\n",
    "Посмотрим на технические параметры загруженных данных для обучения. Для этого вызовем метод `describe()` для набора данных `training_data`\n",
    "\n",
    "Для удобства отображения мы транспонируем результат: меняем местами столбцы и строки."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 390
    },
    "id": "kAoBNr4T5ycD",
    "outputId": "db4a4b51-f9e4-41db-88d2-f8b9c3b94507"
   },
   "outputs": [],
   "source": [
    "training_data.describe().T"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "rx6NLw5b5ycG"
   },
   "source": [
    "Обратим внимание на общие статистики показателей в данных:\n",
    "* **count** -- количество значений, которые не являются пропущенными (`NaN`);\n",
    "* **mean**, **std** -- среднее и разброс данных в соответствующем поле;\n",
    "* остальные статистики -- минимальное и максимальное значения, и квантили."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "iMES1MZQ5ycH"
   },
   "source": [
    "Из таких характеристик столбцов мы уже можем извлечь некоторую информацию о данных:\n",
    "* У столбца **SeriousDlqin2yrs** среднее 0.066. Значит, в нашей выборке только у 6,6% клиентов есть дефолт.\n",
    "* У столбца **MonthlyIncome** заполнено только 40147 значений из 50000. Минимальное значение дохода - 0, максимальное - 3008750.\n",
    "* У столбца **NumberOfDependents** больше половины значений - нулевые."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "00EyTVBq5ycI"
   },
   "source": [
    "# 2. Обработать данные перед обучением модели"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "TaqYZAxE5ycI"
   },
   "source": [
    "**Шаг 2.1. Проверяем данные на наличие пропусков и типов переменных**\n",
    "\n",
    "Начнем с проверки общей информации о данных.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 323
    },
    "id": "CGVrPipY5ycJ",
    "outputId": "7abccb3a-16de-4e0f-e84c-8ca9214b7c69"
   },
   "outputs": [],
   "source": [
    "training_data.info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "euQeNQOT5ycM"
   },
   "source": [
    "**Шаг 2.2. Заполнение пропусков**\n",
    "\n",
    "Рассчитаем средние значения признаков в обучающей выборке, и заполним полученными\n",
    "числами пропуски как в **тестовом наборе** данных, так и в **самой обучающей выборке**.\n",
    "\n",
    "Мы будем заполнять средними значениями из обучающей выборки, так как при решении реальной задачи нам будут доступны только данные для обучения.\n",
    "\n",
    "Для получения средних значений вызовем метод `mean()`. По умолчанию метод считает средним значения по столбцам. После выполнения ячейки средние значения записаны в переменной `train_mean`\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 221
    },
    "id": "9uBmGCVY5ycM",
    "outputId": "44776121-c937-4eb6-f0e2-cd9e9a9fb3c1"
   },
   "outputs": [],
   "source": [
    "train_mean = training_data.mean()\n",
    "train_mean"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "w1R51bB-5ycQ"
   },
   "source": [
    "Пропуски в данных можно заполнять и разными методами:\n",
    "* выборочной статистикой (среднее, медиана);\n",
    "* прогнозами регрессии по известыми признакам;\n",
    "* случайными значениями.\n",
    "\n",
    "Если относительно небольшая доля наблюдению имеет пропуски, то можно\n",
    "вовсе исключить неполные наблюдения с пропущенными значениями из выборки.\n",
    "\n",
    "Для заполнения средним значеним, передадим на вход методу `fillna` полученный ранее набор средних значений для каждого столбца. Опция `inplace=True` говорит, что мы запишем изменения прямо в существующий массив, а не создадим новый."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "v3KNUVij5ycQ"
   },
   "outputs": [],
   "source": [
    "training_data.fillna(train_mean, inplace=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "yy0tqswo5ycV"
   },
   "source": [
    "**Шаг 2.3. Работаем с целевой переменной**\n",
    "\n",
    "*Какая переменная целевая?*\n",
    "\n",
    "В данном случае по условию задачи мы должны прогнозировать дефолт, поэтому целевая переменная - это наличие дефолта. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "zoBuwFIR5ycX"
   },
   "outputs": [],
   "source": [
    "target_variable_name = 'SeriousDlqin2yrs'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "fC7dfuF75ycc"
   },
   "source": [
    "Обратим внимание на целевой признак **SeriousDlqin2yrs** -- наличие серьёзной просрочки\n",
    "по кредитным выплатам за последние два года. Обычно заёмщики стараются производить выплаты\n",
    "вовремя.\n",
    "\n",
    "Чтобы посчитать количество хороших заемщиков без больших просрочек (значение переменной **SeriousDlqin2yrs** равно нулю) и плохих с просрочкой (значение **SeriousDlqin2yrs** равно единице) вызовем метод `value_counts()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 68
    },
    "id": "mki__Z5a5yce",
    "outputId": "37d8148a-c015-4ffa-acc4-f0abb3d3183c"
   },
   "outputs": [],
   "source": [
    "training_data[target_variable_name].value_counts()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "YkI62BHW5yci"
   },
   "source": [
    "Нам нужно выделить в отдельную переменную *training_values* столбец из нашей таблицы, который соответствует определенной выше целевой переменной. Для этого мы у таблицы *training_data* в квадратных скобках указываем имя нужного столбца. В нашем случае это имя записано в переменной *target_variable_name*. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "QlVkHcTv5ycj"
   },
   "outputs": [],
   "source": [
    "training_values = training_data[target_variable_name]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "jyiF_GgO5ycm"
   },
   "source": [
    "Проверим размерность целевой переменной"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "id": "cbXofKHL5ycn",
    "outputId": "e79eaa67-2ea3-4dab-9042-e46a6e2c1930"
   },
   "outputs": [],
   "source": [
    "training_values.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "805NL69f5ycp"
   },
   "source": [
    "Запись `(50000,)` равносильна `(50000, 1)`.  Она означает, что у нас 50000 экземпляров в выборке и 1 признак"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "yhmNvWOJ5ycq"
   },
   "source": [
    "Отделим входные переменные от выходной (целевой), чтобы можно было построить модель предсказания целевой переменной по входным. Для это нужно у переменной training_data вызвать метод drop()."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "arIkbpOu5ycr"
   },
   "outputs": [],
   "source": [
    "training_points = training_data.drop(target_variable_name, axis=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "id": "tDeglfc85ycs",
    "outputId": "00632820-ee01-4806-a247-d2f15d8a6e77"
   },
   "outputs": [],
   "source": [
    "training_data.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "id": "RK8TKuwo5ycv",
    "outputId": "aa900af3-e1cf-4e11-a697-a33d2d0fcf18"
   },
   "outputs": [],
   "source": [
    "training_points.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "fyBa5U8-5ycy"
   },
   "source": [
    "Видно, что столбца действительно нет, а количество строк не изменилось. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "WwTUoxhW5ycy"
   },
   "source": [
    "##   3. Обучить модель на обучающей выборке"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "yjkC8cCH5ycz"
   },
   "source": [
    "**Шаг 3.1. Выбираем метод, который будем использовать**\n",
    "\n",
    "Проще всего начать с простых методов. \n",
    "Мы воспользуемся двумя методами для построения моделей классификации и сравним их между собой:\n",
    "* Логистическая регрессия *logistic regression*\n",
    "* Лес решающих деревьев *random forest*\n",
    "\n",
    "Логистическая регрессия - \"адаптация\" линейной регрессии для решения задачи классификации. Она принадлежит к классу обобщенных линейных моделей.\n",
    "А вот с помощью леса решающих деревьев можно хорошо решать и задачу регрессии, и задачу классификации."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "5uWT4zDr5yc0"
   },
   "source": [
    "Мы импортируем два модуля из **sklearn** библиотеки:\n",
    " * *linear_model* - тут находятся все линейные *и обобщенные линейные* модели, в том числе модель логистической регрессии.\n",
    " * *ensemble* - тут находятся модели на основе ансамблей."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "cGWniaCH5yc0"
   },
   "outputs": [],
   "source": [
    "from sklearn import linear_model, ensemble"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "H0x0p65d5yc7"
   },
   "outputs": [],
   "source": [
    "logistic_regression_model = linear_model.LogisticRegression() # создаем модель"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 102
    },
    "id": "qxrs1oN_5yc9",
    "outputId": "153aa3b3-a541-414b-8dcf-b443a605fb58"
   },
   "outputs": [],
   "source": [
    "logistic_regression_model # смотрим, что получилось"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "logistic_regression_model.get_params()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "HxWO_CHP5ydA"
   },
   "source": [
    "Модель логистической регрессии сложнее, чем модель линейной регрессии. Поэтому параметров у такой модели гораздо больше. Многие из них связаны с тем, с помощью какой процедуры мы будем подбирать параметры модели (*max_iter*, *dual*, *solver*, *tol*, *warm_start*), устойчивостью модели (*C*, *penalty*), тем, что мы решаем задачу классификации, а не регрессии (*class_weight*, *multi_class*)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "yOpnIaw35ydB"
   },
   "source": [
    "Чтобы создать модель случайного леса, пишем имя модуля ensemble, затем точку, затем название модели. \n",
    "\n",
    "Для этого нужно выполнить следующий код:\n",
    "\n",
    "```python\n",
    "random_forest_model = ensemble.RandomForestClassifier()\n",
    "random_forest_model\n",
    "```\n",
    "\n",
    "Код отличается от кода при решении задачи регрессии тем, что теперь нам нужна модель для классификации `RandomForestClassifier`, а не регрессии `RandomForestRegressor`.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "wlgtW_nl5ydB"
   },
   "outputs": [],
   "source": [
    "random_forest_model = ensemble.RandomForestClassifier(n_estimators=100)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 153
    },
    "id": "wBk50LgG5ydE",
    "outputId": "a0a9c7af-e729-4b7e-a01c-f73db95d5c76"
   },
   "outputs": [],
   "source": [
    "random_forest_model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "random_forest_model.get_params()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "7qMVAxeZ5ydI"
   },
   "source": [
    "У модели классификации на основе случайного леса больше параметров. Рассмотрим наиболее важные:\n",
    "* параметр *n_estimators* определяет, сколько деревьев в лесу,\n",
    "* в параметре *max_depth* устанавливается, какая максимальная глубина у дерева,\n",
    "* в параметре *min_samples_leaf* задается, какое минимальное число объектов может попасть в лист дерева."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "5r-rWuo-5ydJ"
   },
   "source": [
    "**Шаг 3.2. Обучить модель**\n",
    "\n",
    "Теперь, когда мы создали прототипы обеих моделей, можем их обучить с помощью обучающей выборки. \n",
    "\n",
    "Для этого вызываем метод **fit()** у каждой модели и передаем ему на вход два аргумента: \n",
    "таблицу входных признаков и столбец значений целевой переменной - (training_points, training_values)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 102
    },
    "id": "8W3-Nf8g5ydK",
    "outputId": "95616127-0537-4628-c216-64a5344a3782"
   },
   "outputs": [],
   "source": [
    "logistic_regression_model.fit(training_points, training_values)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "KwFZfh8b5ydM"
   },
   "source": [
    "Делаем тоже самое для модели решающего леса."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 153
    },
    "id": "w0djXP4P5ydN",
    "outputId": "a3b65096-21d8-40a2-bd22-ddaaa4bfe998"
   },
   "outputs": [],
   "source": [
    "random_forest_model.fit(training_points, training_values)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "beMhsTKQ5ydO"
   },
   "source": [
    "* Для двух разных моделей в sklearn методы для обучения модели не отличаются.\n",
    "* Мы получили две обученные модели. \n",
    "* Теперь необходимо провалидировать модели на новых тестовых данных, которые не использовались при обучении модели."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "tGhylaB65ydP"
   },
   "source": [
    "## 4. Загрузить и предобработать данные для тестирования"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "FFFHXaPZ5ydP"
   },
   "source": [
    "**Шаг 4.1. Загрузим данные для тестирования**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "CafHxOcg5ydQ"
   },
   "outputs": [],
   "source": [
    "test_data = pd.read_csv('test_data_defolt.csv')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "C17rc2605ydS"
   },
   "source": [
    "**Шаг 4.2. Предобработка данных для тестирования**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "H3kB-L_R5ydS"
   },
   "source": [
    "Тестовые данные нужно предобработать аналогично обучающим. Поэтому заполним сперва пропуски в данных.\n",
    "\n",
    "Для заполнения средним значеним, передадим на вход методу `fillna` полученный ранее набор средних значений для каждого столбца. Опция `inplace=True` говорит, что мы запишем изменения прямо в существующий массив, а не создадим новый"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "9HWGt5dt5ydT"
   },
   "outputs": [],
   "source": [
    "test_data.fillna(train_mean, inplace=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "S7qga1Yb5ydV"
   },
   "source": [
    "**Шаг 4.3. Отделяем целевую переменную**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "ZJPVGyur5ydW"
   },
   "outputs": [],
   "source": [
    "test_values = test_data[target_variable_name]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "Yr7Ol_MO5ydY"
   },
   "outputs": [],
   "source": [
    "test_points = test_data.drop(target_variable_name, axis=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "id": "0_mD8QMa5ydZ",
    "outputId": "d68d2342-d907-486c-9d9c-ccac71956159"
   },
   "outputs": [],
   "source": [
    "test_points.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "eZlE_oH45ydb"
   },
   "source": [
    "# 5. Провалидировать модель на тестовой выборке"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "-p8veWCx5ydb"
   },
   "source": [
    "<img src=\"https://drive.google.com/uc?id=1QbHrix_UrbD77BmIGiitiaH8nB8UdUmj\" alt=\"Drawing\" style=\"width: 400px;\"/>\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Kurbyi0n5ydc"
   },
   "source": [
    "Сначала получим прогноз модели на тестовых данных *`test_points`* с помощью моделей логистической регрессии и решающего леса.\n",
    "Для этого для обеих моделей запустим метод **`predict()`**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "TVnx32rB5ydc"
   },
   "outputs": [],
   "source": [
    "test_predictions_logistic_regression = logistic_regression_model.predict(test_points)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "gg4PPJtC5yde"
   },
   "outputs": [],
   "source": [
    "test_predictions_random_forest = random_forest_model.predict(test_points)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "l9_fs-c45ydi"
   },
   "source": [
    "Посмотрим, сколько предсказаний каждого вида (возвратов кредитов и дефолтов) спрогнозировали модели. Для этого необходимо вызвать функцию **`vaue_counts()`** из библиотеки **`pandas`** для полученных прогнозов."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 68
    },
    "id": "TJIg0oAn5ydi",
    "outputId": "29e6d7e8-3445-45d6-a90e-66614c68b174"
   },
   "outputs": [],
   "source": [
    "pd.value_counts(test_predictions_logistic_regression)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 68
    },
    "id": "pMZ2eocK5ydn",
    "outputId": "10dce31d-17a7-42bb-a126-d8a0cc367e8c"
   },
   "outputs": [],
   "source": [
    "pd.value_counts(test_predictions_random_forest)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "8NWiOeAO5ydq"
   },
   "source": [
    "### Шаг 5.1. Точность прогноза"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "B0coP1Xa5ydr"
   },
   "source": [
    "Естественный способ измерить качество модели - посчитать долю правильных предсказаний, то есть, сколько в процентном соотношении от размера тестовой выборки модель угадала единичек и сколько угадала ноликов. Такая метрика называется точность (accuracy). "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "dq1y86vu5ydr"
   },
   "source": [
    "<img src=\"https://drive.google.com/uc?id=1ITTp5pCtDKszhkkLXzXiuCWi09VJaioA\" alt=\"Drawing\" style=\"width: 600px;\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "e9aBjkCB5yds"
   },
   "source": [
    "Функция для подсчета точности реализована в библиотеке **sklearn** и называется **`accuracy_score()`**. Импортируем её. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "AIGg7pNl5ydt"
   },
   "outputs": [],
   "source": [
    "from sklearn.metrics import accuracy_score"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "oaVzimJQ5ydv"
   },
   "source": [
    "В функцию **`accuracy_score()`** необходимо передать два аргумента:\n",
    "* истинные значения меток - *test_values*\n",
    "* предсказания модели - *test_predictions_logistic_regression* или *test_predictions_random_forest*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 51
    },
    "id": "8ukyCHxY5ydw",
    "outputId": "53d7defb-bf89-42dc-de4a-dc9ae5b2766a"
   },
   "outputs": [],
   "source": [
    "print(accuracy_score(test_values, test_predictions_logistic_regression))\n",
    "print(accuracy_score(test_values, test_predictions_random_forest))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "7CwAf3u55yd1"
   },
   "source": [
    "**Как понять, хорошо работает модель или нет?**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "j2rmmswr5yd2"
   },
   "source": [
    "Из значения точности мы никак не можем понять, сколько меток каждого класса правильно предсказала модель. В нашей задаче мало значений с классом 1 (дефолт), но много 0 (возврат кредита). Может быть такая ситуация, когда модель очень хорошо научилась выделять характеристики большого класса, в нашем случае 0, но совсем не умеет выделять характеристики маленького класса. А часто именно последние в большей степени интересуют аналитиков. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "vnUXSb8z5yd3"
   },
   "source": [
    "Самый простой способ проверить - это сравнить значения точности для наших моделей с точностью для константного классификатора, модели, которая всегда бы предсказывала больший класс, в нашем случае 0. \n",
    "Для этого можно в функцию **`accuracy_score()`** в качестве второго аргумента передать массив нулей такого же размера, как и *test_values*. Это делается с помощью функции **`zeros_like()`** из библиотеки numpy, у которой один аргумент - *test_values*, массив с размером которого будет создан массив нулей. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "id": "stJFl6dx5yd3",
    "outputId": "99f0999a-7a60-40af-e984-69e8d7007226"
   },
   "outputs": [],
   "source": [
    "print(accuracy_score(test_values, np.zeros_like(test_values)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "xGmNWcL_5yd5"
   },
   "source": [
    "Действительно, для логистической регрессии точность очень близка к точности константного классификатора. Это не означает, что логистическая регрессия работает определенно плохо, но необходимо проверить дополнительные метрики. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "GdKe23aH5yd5"
   },
   "source": [
    "### Шаг 5.2. Таблица сопряженности модели классификации"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "sxY2MFXW5yd6"
   },
   "source": [
    "Другой способ оценивать качество работы классификатора - использовать таблицу сопряженности. \n",
    "\n",
    "**Таблица сопряжённости** (матрица неточности, или Confusion matrix) содержит сводные показатели качества работы классификатора. **Строки** этой таблицы соответствуют **фактическим классам** тестового набора, а **столбцы** - **предсказанным** классификатором меткам.\n",
    "\n",
    "Импортируем функцию для построения таблицы сопряженности из библиотеки **`sklearn`**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "YZ6q33GC5yd6"
   },
   "outputs": [],
   "source": [
    "from sklearn.metrics import confusion_matrix"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "OmOcyFVh5yd-"
   },
   "source": [
    "Таблица содержит четыре сводных показателя, каждый из которых отражает количество объектов в одной и четырех\n",
    "категорий: \n",
    "* **истинно позитивный** (*True positive*, **TP**) -- объект\n",
    "класса `1` был верно помечен меткой `1`;\n",
    "* **ложно позитивный** (*False positive*, **FP**) -- объект\n",
    "фактически принадлежит классу `0`, но помечен меткой `1`;\n",
    "* **истинно отрицательный** (*True negative*, **TN**) -- классификатор\n",
    "верно определил, что объект класса `0` принадлежит классу `0`;\n",
    "* **ложно отрицательный** (*False negative*, **FN**) -- классификатор\n",
    "пометил объект меткой `0`, однако на самом деле объект принадлежит классу `1`.\n",
    "\n",
    "\n",
    "Замечание: ошибки False positive часто называют **ложной тревогой**, а False negative - **пропуском цели**. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "26ZVEH1P5yd_"
   },
   "source": [
    "|                   |  Предсказано `0` |  Предсказано `1` |\n",
    "|:-------------------|:------------------|:------------------|\n",
    "|**Фактически** `0`  |       TN         |       FP         |\n",
    "|**Фактически** `1`  |       FN         |       TP         | "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "cPRJg0CW5yeA",
    "scrolled": true
   },
   "source": [
    "Посмотрим на таблицу сопряженности для логистической регрессии и случайного леса. Для этого в функцию **`confusion_matrix()`** необходимо передать два аргумента:\n",
    "* истинные значения меток - *test_values*\n",
    "* предсказания модели - *test_predictions_random_forest*\n",
    "\n",
    "Далее для удобства мы запишем полученную матрицу в удобный табличный вид, воспользовавшись функцией **`DataFrame()`** из библиотеки **pandas**. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 111
    },
    "id": "PzjpLH4adsXt",
    "outputId": "9bb327cc-e96b-4409-bbfb-853491f70441"
   },
   "outputs": [],
   "source": [
    "logistic_regression_confusion_matrix = confusion_matrix(test_values, test_predictions_logistic_regression)\n",
    "logistic_regression_confusion_matrix = pd.DataFrame(logistic_regression_confusion_matrix)\n",
    "\n",
    "logistic_regression_confusion_matrix"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 111
    },
    "id": "y2MCVPIy5yeC",
    "outputId": "837858b1-97db-4162-93f5-59f827e7c672"
   },
   "outputs": [],
   "source": [
    "random_forest_confusion_matrix = confusion_matrix(test_values, test_predictions_random_forest)\n",
    "random_forest_confusion_matrix = pd.DataFrame(random_forest_confusion_matrix)\n",
    "\n",
    "random_forest_confusion_matrix"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "wX_FXro15yeF"
   },
   "source": [
    "Разберем полученные значения подробнее:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "mIfEqxCo5yeJ"
   },
   "source": [
    "<img src=\"https://drive.google.com/uc?id=1xBlpY2UwXy94IYxAme4OXZxxaNm57A3x\" alt=\"Drawing\" style=\"width: 400px;\" width=\"700\"/>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "lMCGbSJr5yeK"
   },
   "source": [
    "Почему значения на картинке отличаются от тех, которые мы получили?\n",
    "\n",
    "`random_seed` не был зафиксирован в модели Случайных лесов, и мы видим незначительные отклонения в результатах предсказания."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "isaLYaqS5yeq"
   },
   "source": [
    "### *Бонус:  ROC кривая классификатора"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "fSe79hj75yeq"
   },
   "source": [
    "Если хотят сравнить метрики на разных наборах данных, обычно работают не с абсолютными значениями True Positive и False Positive, а с их долями:\n",
    "\n",
    "* Доля ложноположительных срабатываний $\\text{FPR} = \\frac{FP}{FP + TN}$;\n",
    "* Доля истинно положительных срабатываний $\\text{TPR} = \\frac{TP}{TP + FN}$.\n",
    "\n",
    "Заметим, что $FP + TN$ дает общее число объектов класса $0$, а $TP + FN$ - общее число объектов класса $1$. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "f5nm_bAR5yeq"
   },
   "source": [
    "Одной из самых популярных метрик для задачи классификации является ROC кривая. ROC расшифровывается как *Receiver Operating Characteristic*. Эта кривая наглядно показывает зависимость доли истинно позитивных срабатываний (**TPR**) от доли ложно позитивных срабатываний (**FPR**) при изменении порога классификации."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "twvcSW7V5yer"
   },
   "source": [
    "Функция **roc_curve()** из **scikit-learn** позволяет получить координаты точек ROC кривой, а также значения порога **threshold**, при котором достигается соответствующие значения метрик **FPR** и **TPR**.\n",
    "\n",
    "На вход функции **roc_curve()** необходимо передать два аргумента:\n",
    "* истинные значения меток - *test_values*\n",
    "* вероятности, предсказанные моделью - *test_probabilities*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Вместо прогноза меток классов модель может с помощью метода **`predict_proba()`** выдавать метки вероятности принадлежности к классам.\n",
    "Так как класса у нас 2: заемщики с дефолтом и без, то матрица будет размером **(количество объектов в тестовой выборке, 2)**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_probabilities = logistic_regression_model.predict_proba(test_points)\n",
    "#test_probabilities = random_forest_model.predict_proba(test_points)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Посмотрим на первые пять значений этой матрицу: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_probabilities[:5, :]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Вероятность принадлежности ко второму классу - во втором столбце матрицы вероятностей."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_probabilities = test_probabilities[:, 1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "rePH4FgT5yes"
   },
   "outputs": [],
   "source": [
    "false_positive_rates, true_positive_rates, threshold = roc_curve(test_values, test_probabilities)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "oVSqwTNo5yet"
   },
   "source": [
    "Нарисуем кривую"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 458
    },
    "id": "Dmkvvm455yeu",
    "outputId": "6a3a95c0-a903-45e1-d22e-8d32cb8ff8dc"
   },
   "outputs": [],
   "source": [
    "# создаём график\n",
    "plt.figure(figsize=(7, 7))\n",
    "\n",
    "# рисуем кривую\n",
    "plt.plot(false_positive_rates, true_positive_rates, label='Сглаженные значения ROC-AUC')\n",
    "\n",
    "# кривая, соответствующая случайному угадыванию\n",
    "plt.plot([0, 1], [0, 1], color='k', lw=2, linestyle=':', label='Модель, выдающая случайное значение')\n",
    "\n",
    "plt.title('ROC curve')\n",
    "plt.xlabel('False Positive Rate')\n",
    "plt.ylabel('True Positive Rate')\n",
    "\n",
    "plt.fill_between(false_positive_rates, true_positive_rates, alpha=0.4, label='Площадь под кривой (ROC-AUC)')\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "rqV1cHuj5yew"
   },
   "source": [
    "Чем ближе в целом кривая **ROC** к **левому верхнему** углу, тем лучше качество классификации."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "4uBWO7Bt5yex"
   },
   "source": [
    "Несмотря на наглядность, иногда требуется некоторое число, обобщающее весь\n",
    "график. Для ROC кривой таким числом является \"площадь под кривой\" (**ROC-AUC**). \n",
    "\n",
    "В **sklearn** есть специальная функция **roc_auc_score()** для подсчёта\n",
    "площади под ROC-кривой. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "PZFwHXTZ5yex"
   },
   "outputs": [],
   "source": [
    "from sklearn.metrics import roc_auc_score"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Z5lZEu1V5ye3"
   },
   "source": [
    "Типичная шкала для **ROC-AUC** (часто все зависит от задачи):\n",
    "* $0.90$ - $1.00$ отлично;\n",
    "* $0.80$ - $0.90$ хорошо;\n",
    "* $0.70$ - $0.80$ удовлетворительно;\n",
    "* $0.60$ - $0.70$ плохо;\n",
    "* $0.50$ - $0.60$ очень плохо;\n",
    "* $0.00$ - $0.50$ классификатор перепутал метки."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "id": "a5uXMrEs5ye3",
    "outputId": "7c1a5ce9-509f-4cd0-afba-fbae05c6a149",
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "roc_auc_value = roc_auc_score(test_values, test_probabilities)\n",
    "\n",
    "print(\"ROC-AUC на тестовой выборке:\", roc_auc_value) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "colab": {
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.16"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
